package it.polito.connectedcar.message;

import it.polito.connectedcar.tcc.zone.Token;

/**
 * Abstract class representing an incoming message coming from other entities.
 * It deals with authentication and integrity checks by means of digests, before
 * eventually proceeding with the datagram unpacking. Actually it wraps a
 * datagramPacket and it uses the {@link Storage} class to read various data
 * type from a byte array source.
 * 
 * the onProcessMessage method will be called if the validation of the digest
 * succeeded. A subclass extending this class will have to implement that method
 * in order to extract the meaningful data from the Storage. Each subclass must
 * define a messageCode (actionCode) which uniquely identifies its kind of
 * message in the application. Since many entities in the system are able to
 * send and receive messages , also the sourceTypeCode byte has to be passed to
 * the superclass in order to say which entity is concerning with. In case of
 * IncomingMessage , the sourceTypeCode and actionCode will be extracted
 * automatically by this class and made available to all extensions during the
 * onProcess method, and they should be used to semantically verify the
 * correcteness of the content present in the message
 * 
 * 
 * @author Nicola Aresta
 * 
 */
public abstract class IncomingMessage extends Message {

    private static final int HEADER_SIZE = 8 + 1 + 4;// ( = timestamp , 1 codes
						     // , 4= payloadsize
    private static final int SHA_512_SIZE = 64;
    private int payloadSize;
    private byte[] payload;
    private byte[] digest;

    public IncomingMessage(byte[] data, byte sourceTypeCode) {
	super(data, sourceTypeCode);
    }

    /**
     * In order to be read , the message should be first verified against
     * integrity and authentication , to be sure the message owner has the
     * rights to send this message
     * 
     * @param t
     * @return
     * @throws Exception
     */
    public final boolean processMessage(Token t) throws Exception {
	// storage.reset();

	this.timestamp = storage.readLong();
	short codes = storage.readByte();
	this.sourceTypeCode = codes >> 4;
	this.actionCode = codes & 0x0f;

	this.payloadSize = storage.readInt();
	if (storage.size() != (HEADER_SIZE + payloadSize))
	    throw new Exception("invalid packet size : expected "
		    + (HEADER_SIZE + payloadSize) + " but was "
		    + storage.size());

	// payload = new byte[payloadSize];

	// extract payload

	// for the time being , the Digest is not used

	/*
	 * if(storage.size()!=(HEADER_SIZE+payloadSize+SHA_512_SIZE)) throw new
	 * Exception("invalid packet"); for(int i=0;i<payloadSize;i++) {
	 * payload[i]=(byte) storage.readByte(); } //extract digest digest = new
	 * byte[SHA_512_SIZE]; for(int i = 0;i<SHA_512_SIZE;i++)
	 * digest[i]=(byte) storage.readByte();
	 * 
	 * //check digest on message
	 * 
	 * final SecretKeySpec secretKey = new SecretKeySpec( t.getBytes(),
	 * "HmacSHA512" ); Mac hMac = Mac.getInstance("HmacSHA512");
	 * hMac.init(secretKey);
	 * hMac.update(ByteBuffer.allocate(HEADER_SIZE).putLong
	 * (timestamp).putInt(payloadSize).array()); hMac.update(payload);
	 * byte[] finalDigest = hMac.doFinal();
	 * if(finalDigest.length!=digest.length) { return false; }
	 * 
	 * for(int i=0;i<finalDigest.length;i++) if(finalDigest[i]!=digest[i])
	 * return false;
	 */

	/*
	 * storage.reset(); for(int i=0;i<payloadSize;i++) {
	 * storage.writeByte(payload[i]); }
	 */

	return onProcessMessage(storage);

    }

    /**
     * Any specific extension of this abstract class should give a way to
     * extract coherently bytes from the Storage. This method will be called
     * when the message integrity and authentication has already been verified
     * 
     * @param st
     *            will contain the payload of the whole message
     * @return true if the payload contains semantically correct data , false
     *         otherwise
     */
    protected abstract boolean onProcessMessage(Storage st);

    protected abstract void convertFromData(Storage st);

    public byte[] getMessagePayload() throws Exception {
		return payload;
	}
}
